import Layout, { Point } from '../../core/Layout';
import { ComponentInterface } from '../../core/Component';
import Screen from '../../core/Screen'
import CanvasComponent from './CanvasComponent';

export default abstract class CanvasScreen extends Screen {
  context: CanvasRenderingContext2D
  element: HTMLCanvasElement
  landscape?: boolean
  globalAlpha: number

  constructor(options: {
    children?: ComponentInterface[],
    element?: HTMLCanvasElement,
  }) {
    super(options);
    this.element = options.element
    this.globalAlpha = 1
  }

  abstract build() : ComponentInterface | ComponentInterface[]

  async init(): Promise<void> {
    if(!this.element) {
      this.element = document.createElement('canvas')
      this.element.style.position = 'absolute'
      this.element.style.top = '0'
      this.element.style.left = '0'
      this.context = this.element.getContext('2d')
      document.body.appendChild(this.element)
    }

    var childOrChildren = this.build()
    this.children = Array.isArray(childOrChildren) ? childOrChildren : [childOrChildren]

    await super.init()
    this.resize()
  }

  async load(): Promise<void> {
    await super.load()
    this.element.addEventListener('touchstart', this.touchStart.bind(this))
    this.element.addEventListener('touchend', this.touchEnd.bind(this))
    this.element.addEventListener('touchcancel', this.touchCancel.bind(this))
    this.element.addEventListener('touchmove', this.touchMove.bind(this))
  }

  async unload(): Promise<void> {
    this.element.removeEventListener('touchstart', this.touchStart.bind(this))
    this.element.removeEventListener('touchend', this.touchEnd.bind(this))
    this.element.removeEventListener('touchcancel', this.touchCancel.bind(this))
    this.element.removeEventListener('touchmove', this.touchMove.bind(this))
    await super.unload()
  }

  async resize(): Promise<void> {
    this.element.width = this.application.screenWidth
    this.element.height = this.application.screenHeight
    this.landscape = window.innerWidth > window.innerHeight
    if(this.application.landscapeMode && !this.landscape) {
        this.element.style.transform = 'rotate(90deg) translate(0, -100%)'
        this.element.style.transformOrigin = '0 0'
    } else {
      this.element.style.transform = 'none'
      this.element.style.transformOrigin = '0 0'
    }
    await super.resize()
  }

  async update(dt: number): Promise<void> {
    // 清除画布
    this.context.clearRect(0, 0, this.application.screenWidth, this.application.screenHeight)
    this.context.globalAlpha = this.globalAlpha 
    await super.update(dt)
  }
  /**
   * 获取 CanvasComponent
   */
  getCanvasComponents() {
    var components: CanvasComponent[] = []
    function filterCanvasComponents(children: ComponentInterface[]) {
      children.forEach(child => {
        if(child instanceof Layout) {
          filterCanvasComponents(child.children)
        }
        if(child instanceof CanvasComponent) {
          components.push(child)
        }
      });
    }
    filterCanvasComponents(this.children)
    return components
  }

  touchStart(evt: TouchEvent) {
    var components = this.getCanvasComponents()
    components.forEach((component) => {
      if(component.handlers) {
        component.handlers.touchStart(evt)
      }
    })
  }

  touchEnd(evt: TouchEvent) {
    var components = this.getCanvasComponents()
    components.forEach((component) => {
      if(component.handlers) {
        component.handlers.touchEnd(evt)
      }
    })
  }

  touchCancel(evt: TouchEvent) {
    var components = this.getCanvasComponents()
    components.forEach((component) => {
      if(component.handlers) {
        component.handlers.touchCancel(evt)
      }
    })
  }

  touchMove(evt: TouchEvent) {
    var components = this.getCanvasComponents()
    components.forEach((component) => {
      if(component.handlers) {
        component.handlers.touchMove(evt)
      }
    })
  }

  transformPoint(point: Point) : Point {
    if(this.application.landscapeMode && !this.landscape) {
      var screenHeight = this.application.screenHeight
      return new Point(point.y, screenHeight - point.x)
    }
    return point
  }
}